local util = require("dx_util")
local channel = require("channel_util")
local log = require("dx_logger")
local dx_queue = require("dx_queue")
local channel_service = require("channel_service")


------------变量区-------------------
-- 定义相关命令头和命令字
CMD_HEAD1 = "aa"
CMD_HEAD2 = "55"

-- 串口监听地址
OPEN_A = "/dev/ttyS0"
OPEN_B = "/dev/ttyS1"
-- 读取串口超时时间
TIME_OUT = 30

------------函数区-------------------
-- 发送串口数据通用接口
local function channel_send(cmd, data, tlv_flag)
    if tlv_flag then
        local pack = {CMD_HEAD1, CMD_HEAD2, cmd}
        local length_hex = util.number_to_hex((#data / 2))
        local temp = length_hex .. data
        temp:gsub(
            "..",
            function(cc)
                table.insert(pack, cc)
            end
        )
        local crc = util.crc(pack)
        table.insert(pack, crc)
        print("send tlvdata:", table.concat(pack))
        channel.send(vbar_channel, pack, #pack)
    else
        local pack = {}
        data:gsub(
            "..",
            function(cc)
                table.insert(pack, cc)
            end
        )
        channel.send(vbar_channel, pack, #pack)
        print("send:", data)
    end
end




-- 该方法用于判定读取字符是否是指定命令头（55AA）
local function is_pack_head(vbar_channel)
    local buffer = channel.recv(vbar_channel, 1, TIME_OUT)
    if #buffer <= 0 then
        return false
    end
    if buffer[1] == CMD_HEAD1 then
        buffer = channel.recv(vbar_channel, 1, TIME_OUT)
        if buffer[1] == CMD_HEAD2 then
            return true
        else
            return false
        end
    else
        return false
    end
end

local function get_size(str)
    if str == nil or #str <1 then
        return nil
    end
    return tonumber(string.sub(str,3,4), 16) * 256 + tonumber(string.sub(str,1,2), 16)
end

local function fill(str, fill_count)
    if #str < fill_count then
        while #str < fill_count do
            str = "0" .. str
        end
    end
    return str
end


-- 监听串口，接收数据包
local function read_packet(vbar_channel)
    -- 判断数据头部是不是以55 AA开头
    local pack_head_flag = is_pack_head(vbar_channel)
    if pack_head_flag ~= true then
        return
    end

    local pack = {}
    pack["head"] = {}
    table.insert(pack["head"], CMD_HEAD1)
    table.insert(pack["head"], CMD_HEAD2)

    -- 命令头已解析完，读取命令字（占用1Byte）
    local buffer_cmd = channel.recv(vbar_channel, 1, TIME_OUT)
    if #buffer_cmd <= 0 then
        return
    end

    pack["cmd"] = buffer_cmd

    -- 读取结果字（占用1Byte）
    local result = channel.recv(vbar_channel, 1, TIME_OUT)
    pack["result"] = result

    -- 命令头已解析完，读取长度字（占用4Byte）
    local buffer_dlen = channel.recv(vbar_channel, 2, TIME_OUT)

    if #buffer_dlen <= 0 then
        return
    end

    -- 解析长度字，获取数据域长度
    local data_size = get_size(buffer_dlen)
    if data_size <= 0 then
        return
    end
    pack["dlen"] = data_size

    -- 根据长度字读取指定数据长度
    pack["data"] = channel.recv(vbar_channel, data_size, TIME_OUT, buffer_cmd[1])

    -- 读取1Byte的校验位 ，暂时不校验
    pack["crc"] = channel.recv(vbar_channel, 1, TIME_OUT)


    local ret = channel_service["execute_cmd_"..fill(pack["cmd"], 2)](pack)
    return ret
end

-- 监听信道，处理接收到的数据
local main = function()
    -- 将channel_send函数注册给channel_service使用
    channel_service.registere_func("channel_send", channel_send)

    -- 打开信道，并返回信道句柄
    _G.vbar_channel = channel.open(OPEN_A)

    while true do
        util.sleep(0.01)
        read_packet(vbar_channel)
    end
end

------------主流程-------------------
main()





















































local dx_map = require("dx_map")
local log = require("dx_logger")
local decode = require("utils.dx_decode")
local channel = require("channel")
local config = require("config")
local dx_json = require("dx_json")
local time = require("utils.time")
local dx_queue = require("dx_queue")
local serialport_service = require("service.serialport_service")
local serialport = {}

-- 加载全局map资源
local _G_map = _G["_G_map"]
-- 加载全局queue资源
_G.mqtt_queue = dx_map.get(_G_map, "mqtt_queue")
-- 加载全局sqlite实例
-- _G.sqlite_client = dx_map.get(_G_map, "sqlite_client")
_G.serialport_queue = dx_map.get(_G_map, "serialport_queue")

-- 定义相关命令头和命令字
PRO_HEAD1 = "55"
PRO_HEAD2 = "AA"
PRO_CMD_04 = "04"

-- 串口返回数据结果cmd
PROTOCOL_CMD_GET_SCAN_RESULT_2 = "33"

BASE_PATH = "/dev/ttyS0"

local index = 0

-- local test_data = {
--     " ",
--     "55",
--     "AA",
--     "33",
--     "9",
--     "0",
--     "82",
--     "34",
--     "37",
--     "36",
--     "36",
--     "39",
--     "30",
--     "35",
--     "33",
--     "4B",
--     " ",
--     " ",
--     " ",
--     " "
-- }

-- 模拟测试
local test_data = {
    " ",
    "55",
    "AA",
    "C2",
    "13",
    "0",
    -- 1Byte为结果状态，0：扫描成功，1：扫描失败
    "1",
    "74",
    "65",
    "73",
    "74",
    "31",
    "32",
    "33",
    "7C",
    "2B",
    "7C",
    "32",
    "30",
    "32",
    "32",
    "30",
    "33",
    "30",
    "36",
    "25",
    " ",
    " ",
    " ",
    " "
}
local test_data1 = {
    " ",
    "55",
    "AA",
    "C3",
    "15",
    "0",
    -- 数据上送交互格式：子设备uuid |+| 命令字 长度（2字节） 交互数据 校验字
    "74",
    "65",
    "73",
    "74",
    "31",
    "32",
    "33",
    "7C",
    "2B",
    "7C",
    -- 下发权限
    "42",
    "14",
    "0",
    "74",
    "65",
    "73",
    "74",
    "31",
    "32",
    "33",
    "8F",
    "DB",
    " ",
    " ",
    " ",
    " "
}
local test_data2 = {
    " ",
    "55",
    "AA",
    "C1",
    "16",
    "0",
    -- 1Byte为结果状态，0：扫描成功，1：扫描失败
    "1",
    -- n-1Byte为蓝牙设备列表，设备唯一标识和RSSI值使用：分隔，
    "74",
    "65",
    "73",
    "74",
    "31",
    "32",
    "33",
    "3A",
    "50",
    "7C",
    "2B",
    "7C",
    "74",
    "65",
    "73",
    "74",
    "31",
    "32",
    "34",
    "3A",
    "5A",
    "F",
    " ",
    " ",
    " ",
    " "
}
local tt
local change = 0

-- 初始化读取配置信息！(所有require("update_config")的.lua文件都需要从线程中读取配置，每次修改都需要覆盖Map中的配置，且需要保证同一时间只能有一个线程操作该键值对)
while true do
    if dx_map.get(_G_map, "g_config_str") ~= nil and dx_map.get(_G_map, "vbar_config_t_str") ~= nil then
        local g_config_str = dx_map.get(_G_map, "g_config_str")
        local vbar_config_t_str = dx_map.get(_G_map, "vbar_config_t_str")
        g_config = dx_json.decode(g_config_str)
        vbar_config_t = dx_json.decode(vbar_config_t_str)
        break
    end
    time.sleep(0.01)
end
log.debug("[串口信道]打印从_G_map中读取的g_config表", dx_json.encode(g_config))

-- 监听信道，处理接收到的数据
_G.serial_port_listen = function()
    -- local vh = {}

    -- 定义接收串口数据的结构体
    local pack = {}

    -- 打开信道，并返回信道句柄
    _G.vbar_channel = channel.open(BASE_PATH)
    log.info("[串口信道", BASE_PATH, "]初始化完成，启动通道监听，当前信道所挂载的路径是：", BASE_PATH)

    while true do
        -- 测试逻辑，可删除
        -- if change == 0 then
        --     tt = test_data
        --     change = 1
        -- elseif change == 1 then
        --     tt = test_data1
        --     change = 2
        -- else
        --     tt = test_data2
        --     change = 0
        -- end

        -- 监听并解析channel数据，要做四次channel_recv()，第一次判断头：55 AA， 第二次接收type，len等配置数据，第三次接收真正的数据，第四次校验最后一位校验位
        pack = serialport.build_pack(vbar_channel, pack)

        if pack ~= nil then
            -- 测试逻辑，可删除
            -- index = 0
            serialport.parse_pack(pack)
        else
            pack = {}
        end
        os.execute("sleep 1")
    end
end

function serialport.vg_pro_recv_pack1(vbar_channel, size)
    local rs_buffer = ""
    if size == 0 then
        return ""
    end
    for i = 1, size, 1 do
        local buffer = channel.recv(vbar_channel, 1, 500)
        if buffer and buffer ~= "" then
            print("--------buffer--------", buffer)
            if #buffer == 1 then
                rs_buffer = rs_buffer .. "0" .. buffer
            else
                rs_buffer = rs_buffer .. buffer
            end
        else
            return rs_buffer
        end
        if i < size then
            rs_buffer = rs_buffer .. ","
        end
    end

    return rs_buffer
end

-- 测试获取信道数据
-- function serialport.vg_pro_recv_pack1(vbar_channel, size)
--     local buffer = ""
--     for i = 1, size, 1 do
--         buffer = buffer .. tt[index + i]
--         if i ~= size then
--             buffer = buffer .. ","
--         end
--     end
--     index = index + size
--     return buffer
-- end

-- 监听串口，接收数据包
function serialport.build_pack(vbar_channel, pack)
    -- 用于接收每次从信道中接受的数据，一般是4char
    local buffer = ""

    -- 判断数据头部是不是以55 AA开头
    local pack_head_flag = serialport.is_pack_head(vbar_channel, 300)
    if pack_head_flag ~= true then
        return nil
    end

    print("===============接收串口数据解析：=============")
    pack["head"] = {}
    table.insert(pack["head"], PRO_HEAD1)
    table.insert(pack["head"], PRO_HEAD2)

    -- 命令头已解析完，读取命令字和长度字（占用3Byte）
    local buffer = serialport.vg_pro_recv_pack1(vbar_channel, 4)
    if buffer == nil then
        return nil
    end

    -- 根据4Byte解析命令字和长度
    local data_size
    for k, v in pairs(decode.split(buffer, ",")) do
        if k == 1 then
            pack["cmd"] = v
        elseif k == 2 then
            pack["result"] = v
        elseif k == 3 then
            data_size = decode.ConvertStr2Dec(v, 16)
        elseif k == 4 then
            pack["dlen"] = decode.ConvertStr2Dec(v, 16) * 256 + data_size
        end
    end
    log.debug("[串口输入]读取数据长度：", pack["dlen"], "命令字：", pack["cmd"])

    pack["data"] = ""
    pack["crc"] = ""

    -- 根据长度字读取指定数据长度
    pack["data"] = serialport.vg_pro_recv_pack1(vbar_channel, pack["dlen"])
    pack["metadata"] = pack["data"]

    -- 读取1Byte的校验位
    pack["crc"] = serialport.vg_pro_recv_pack1(vbar_channel, 1)

    -- 根据校验位进行crc校验
    local validSuccess = serialport.is_valid_pack(pack)

    log.debug("[串口输入]读取校验位：", pack["crc"], "校验结果：", validSuccess)

    if validSuccess ~= true then
        return nil
    end

    print("head", dx_json.encode(pack["head"]))
    print("cmd", pack["cmd"])
    print("result", pack["result"])
    print("dlen", pack["dlen"])
    print("data", pack["data"])
    print("metadata", pack["metadata"])
    print("crc", pack["crc"])
    return pack
end

-- 该方法直到读到55AA标识符才跳出循环
function serialport.is_pack_head(vbar_channel, timeout)
    local flag = false
    local buffer = ""
    -- 循环读取命令头55标识
    while buffer ~= PRO_HEAD1 do
        buffer = serialport.vg_pro_recv_pack1(vbar_channel, 1)
        dx_map.set(_G_map, "serialport_dog", "Y")
        -- 到发送数据
        local mqtt_msg = dx_queue.pop(serialport_queue, 10)
        if mqtt_msg ~= nil then
            local receive_data = dx_json.decode(mqtt_msg)
            serialport.channel_send(receive_data.cmd, receive_data.data)
        end
    end

    -- 循环读取命令头AA标识
    local isBreak = false
    while true do
        while buffer ~= PRO_HEAD2 do
            buffer = serialport.vg_pro_recv_pack1(vbar_channel, 1)
            log.debug("[串口输入", BASE_PATH, "]读取第二个命令头：", buffer)
            if buffer == "" then
                isBreak = true
                break
            end
            if buffer ~= "" and PRO_HEAD1 == buffer then
                -- 仅仅退出内层循环，相当于continue
                isBreak = false
                break
            elseif buffer ~= "" and PRO_HEAD2 == buffer then
                flag = true
                isBreak = true
                break
            end
        end
        if isBreak == true then
            break
        end
    end
    return flag
end

-- 校验尾部1字节的校验位
function serialport.is_valid_pack(pack)
    local cmd = decode.ConvertStr2Dec(pack["cmd"], 16)
    local resutlNum = 0
    local resutlNum = decode.ConvertStr2Dec(pack["result"], 16)
    local dlen = pack["dlen"]
    local crc = cmd
    if pack["crc"] ~= nil or pack["crc"] ~= "" then
        local dataArray = decode.split(pack["data"], ",")

        crc = bit.bxor(crc, PRO_HEAD_NUM1)
        crc = bit.bxor(crc, (bit.band(bit.rshift(dlen, 0), 0xff)))

        crc = bit.bxor(crc, PRO_HEAD_NUM2)
        crc = bit.bxor(crc, (bit.band(bit.rshift(dlen, 8), 0xff)))
        for i, v in ipairs(dataArray) do
            local num = decode.ConvertStr2Dec(v, 16)
            crc = bit.bxor(crc, num)
        end
        local hexCrc = decode.ConvertStr2Dec(pack["crc"], 16)
        crc = bit.bxor(crc, resutlNum)
        log.debug("传入的校验位：", hexCrc, "正确的校验位：" .. crc)
        if crc ~= hexCrc then
            return false
        end
    else
        return false
    end
    return true
end

-- 到service层
function serialport.parse_pack(pack)
    local method_name = dx_map.get(serialport_service.method_set_map, pack["cmd"])
    if method_name ~= nil and method_name ~= "" then
        local ret = serialport_service[method_name](pack)
        if not ret then
            log.warn("串口处理异常pack：" .. dx_json.encode(pack))
        end
    else
        log.debug("[串口输入]没有找到指令，不支持该指令：", pack["cmd"])
    end
end

function serialport.fill(str, fill_count)
    if #str < fill_count then
        while #str < fill_count do
            str = "0" .. str
        end
    end
    return str
end

--发送串口数据通用接口
function serialport.channel_send(cmd, data)
    local pack = {}

    pack["head"] = PRO_HEAD1 .. PRO_HEAD2
    pack["cmd"] = cmd

    pack["data"] = data
    -- 根据data数据计算长度给到dlen
    local dlen = decode.ConvertDec2X(#data / 2, 16)
    for i = 1, 4 - #dlen, 1 do
        dlen = "0" .. dlen
    end
    if #data / 2 > 65535 then
        return log.error("串口data过长")
    end
    len1 = string.sub(dlen, #dlen - 1, #dlen)
    len2 = string.sub(dlen, #dlen - 3, #dlen - 2)

    pack["dlen"] = #data / 2

    local crc = serialport.create_valid(pack)
    pack["crc"] = crc

    local req = pack["head"] .. pack["cmd"] .. len1 .. len2 .. pack["data"] .. pack["crc"]

    local len = #req / 2

    log.info("发送串口数据：", req)
    print("===============发送串口数据解析：=============", req)
    print("head", pack["head"])
    print("cmd", pack["cmd"])
    print("dlen", pack["dlen"])
    print("len1 .. len2", len1 .. len2)
    print("data", pack["data"])
    print("crc", pack["crc"])
    local ret = channel.send(vbar_channel, req, len)
    print("ret", ret)
    return ret
end

-- 生成校验位
function serialport.create_valid(pack)
    local cmd = decode.ConvertStr2Dec(pack["cmd"], 16)
    -- local dlen = decode.ConvertStr2Dec(pack["dlen"], 16)
    local dlen = pack["dlen"]
    local crc = cmd

    crc = bit.bxor(crc, PRO_HEAD_NUM1)
    crc = bit.bxor(crc, (bit.band(bit.rshift(dlen, 0), 0xff)))

    crc = bit.bxor(crc, PRO_HEAD_NUM2)
    crc = bit.bxor(crc, (bit.band(bit.rshift(dlen, 8), 0xff)))

    local i = 1
    local data = pack["data"]
    while i <= #data do
        local j = i + 1
        local num = decode.ConvertStr2Dec(string.sub(data, i, j), 16)
        crc = bit.bxor(crc, num)
        i = j + 1
    end
    return decode.ConvertDec2X(crc, 16)
end

serial_port_listen()

return serialport
